package list;

import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;


/**
* todo here
* změňte dle potřeby i hlavičku
 */
class MyListItem <E extends Object> {

     MyListItem<E> previous=null ;
     MyListItem<E> next=null;
     E value;

    public MyListItem(E val) {
        
    value=val;
    
    
    
    }

    public MyListItem<E> getPrevious() {
        return previous;
    }

    public void setPrevious(MyListItem<E> previous) {
        this.previous = previous;
    }

    public MyListItem<E> getNext() {
        return next;
    }

    public void setNext(MyListItem<E> next) {
        this.next = next;
    }

    public E getValue() {
        return value;
    }

    public void setValue(E value) {
        this.value = value;
    }
     
    
    
}

/**
* todo here
* změňte dle potřeby i hlavičku
 */
public class MyList<T extends Object> implements Iterable {
   
    
    MyListItem<T> last=null;
    MyListItem<T> first=null;

//todo here

    /**
     * Vlozi prvek na konec seznamu
     * @param value vkladany prvek
     */
    public void addLast(T value) {
        if(last==null){
            last=new MyListItem<T>(value);
            first=last;
        
        }else{
            last.setNext(new MyListItem<T>(value));
            last.getNext().setPrevious(last);
            last=last.getNext();
                
        }
    }

    /**
     * Vlozi prvek na zacatek seznamu
     * @param value vkladany prvek
     */
    public void addFirst(T value) {
       if(last==null){
           first=new MyListItem<T>(value);
           last=first;
           return;
       }
        
       
       
        first.setPrevious(new MyListItem<T>(value));
        first.getPrevious().setNext(first);
        first=first.getPrevious();
    }

    /**
     * Vytvori list-iterator pro pruchod seznamem
     * NEMENTE
     * @return instanci list-iteratoru
     */
    public ListIterator<T> listIterator() {
        return new MyIterator<T>(this);
    }

    /**
     * Vytvori iterator pro pruchod seznamem
     * NEMENTE
     * @return instanci iteratoru
     * @see #listIterator()
     */
    public Iterator<T> iterator(){
        return listIterator();
    }
}


/**
 * Iterator pro pruchod seznamem
 * @param <T> typ vkladaneho prvku
 */
class MyIterator<T> implements ListIterator<T> { // v hlavičce je hint :)
     MyListItem<T> current=null;
     String lastCall="";

    /**
     * @param list odkaz na iterovany seznam
     */
    MyIterator(MyList<T> list){
           current=list.first;
                   
    }

    /**
     * @return true, pokud je za pozici iteratoru dalsi prvek
     */
    public boolean hasNext() {
     
        if(current!=null){
        return current.next!=null;
            }else{return false;}
    }

    /**
     * Vrati prvek za pozici iteratoru a posune o jednu pozici dopredu
     * @return dalsi prvek za pozici iteratoru
     * @throws NoSuchElementException pokud takovy prvek neexistuje
     */
    public T next() {
        if(!hasNext()){throw new NoSuchElementException();}
        if(lastCall!="n"&&lastCall!="a"){
            lastCall="n";
            return current.getValue();
        }
        
       lastCall="n";
       MyListItem<T> a=current.next;
       
        current=current.next; 
       
       return a.getValue();
    }

    /**
     * @return True, pokud je pred pozici iteratoru prvek
     */
    public boolean hasPrevious() {
        
        if(current!=null){
            if(lastCall=="n"||lastCall=="a"){return true;}
        return current.previous!=null;
            }else{return false;}
    }

    /**
     * Vrati prvek pred iteratorem a posune pozici iteratoru o jednu zpet.
     * @return vraci predchozi prvek pred iteratorem
     * @throws NoSuchElementException pokud takovy prvek neexistuje
     */
    public T previous() {
       if(!hasPrevious()){throw new NoSuchElementException();}
       if(lastCall!="p"&&lastCall!="a"){
       
           lastCall="p";
           return current.getValue();
       
       }
       
       lastCall="p";
      
       MyListItem<T> a=current.previous;
      
        current=current.previous; 
       
       return a.getValue();
    }

    /**
     * Vraci pozici prvku, ktery by byl precteny dalsim volanim next.
     * Prvni prvek je 0, posledni n-1. Pokud je iterator za poslednim prvkem
     * (to zahrnuje i prazdny seznam), vrati n (prazdny -> n=0), kde n je
     * pocet prvku seznamu.
     * @see #next()
     * @return pozici prvku, ktery ma byt precten pomoci next
     */
    public int nextIndex() {
        return 0;
    }

    /**
     * Vraci pozici prvku, ktery by byl precten pomoci dalsiho volani previous.
     * Prvni prvek je 0, posledni n-1. Pokud je iterator pred prvni prvekem
     * (to zahrnuje i prazdny seznam), vrati -1. n je pocet prvku seznamu.
     * @see #previous()
     * @return pozici prvku, ktery ma byt precten pomoci previous
     */
    public int previousIndex() {
        return 0;
    }

    /**
     * Vyjme ze seznamu prvek, ktery byl naposled precten operaci next nebo
     * previous.
     * @see #next()
     * @see #previous()
     * @throws IllegalStateException v pripade, ze nebylo volano next ani previous,
     * nebo byla po nich volana metoda remove nebo add.
     */
    public void remove() {
       current.previous.setNext(current.next);
       current.next.setPrevious(current.previous);
       lastCall="r";
    }

    /**
     * Nastavi novou hodnotu prvku, ktery byl naposled precten operaci next nebo
     * previous.
     * @param e nova hodnota prvku
     * @see #next()
     * @see #previous()
     * @throws IllegalStateException v pripade, ze nebylo volano next ani previous,
     * nebo byla po nich volana metoda remove nebo add.
     */
    public void set(T e) {
        if(lastCall!="n"&&lastCall!="p"){
            throw new IllegalStateException();
        }
        current.setValue(e);
        
        
    }

    /**
     * Na misto iteratoru se vlozi novy prvek. Iterator bude ukazovat ZA vlozeny
     * prvek. Tudiz se zvetsi indexy o 1, pripadne volani previous vrati prave
     * vlozeny prvek, hodnotu volani next to neovlivni.
     * @param e hodnota vkladaneho prvku
     */
    public void add(T e) {
        if(current==null){
           MyListItem toAdd=new MyListItem<T>(e);
           current=toAdd;
           lastCall="a";
           return;
        }
        MyListItem toAdd=new MyListItem<T>(e);
        toAdd.setPrevious(current.previous);
        toAdd.setNext(current);
        current.setPrevious(toAdd);
        lastCall="a";
       // current=current.previous;
        //todo here
    }
}

